#include "..\..\NahidaProject-UnitTest\Sources\NahidaUnitTest.h"

#include "..\Sources\EventBus.h"
#include "..\Sources\MemoryPool.h"
#include "..\Sources\SignalAndSlot.h"
#include "..\Sources\ThreadPool.h"

SetEventBusEvent(TestEvent);

class HandlerTest : public NahidaProject::EventBus::EventBusHandler<TestEvent> {
private:
	bool _success;
public:
	HandlerTest() : _success(false) {

	}

	void OnEvent(const TestEvent&) override {
		_success ^= true;
	}

	inline bool isSuccess() const {
		return _success;
	}
};

constexpr float Calculate(float value, int scalar) {
	return value * static_cast<float>(scalar);
}

TESTCASE(EventBusTest) {
	NahidaProject::EventBus::EventBus eventBus;
	HandlerTest handler;
	TestEvent event;

	eventBus.Subscribe(handler);
	eventBus.Publish(event);

	if (!handler.isSuccess()) {
		std::cerr << "Failed to execute the handler" << std::endl;
	}

	eventBus.Unsubscribe(handler);
	eventBus.Publish(event);
	if (!handler.isSuccess()) {
		std::cerr << "Failed to unsubscribe" << std::endl;
	}

	std::cout << "Executed the handler" << std::endl;
}

TESTCASE(MemoryPoolTest) {
	NahidaProject::MemoryPool pool(32, 10);
	JUDGE_EQUAL(pool.PrintFreeBlocks(), 10);
	void* ptr1 = pool.Allocate();
	void* ptr2 = pool.Allocate();
	void* ptr3 = pool.Allocate();
	JUDGE_EQUAL(pool.PrintFreeBlocks(), 7);
	pool.Deallocate(ptr1);
	pool.Deallocate(ptr2);
	JUDGE_EQUAL(pool.PrintFreeBlocks(), 9);
	void* ptr4 = pool.Allocate();
	JUDGE_EQUAL(pool.PrintFreeBlocks(), 8);
}

TESTCASE(SignalAndSlotTest) {
	NahidaProject::Signal<int> signal;
	auto connection = signal.Connect([](int a) {
		std::cout << "Current Number is " << a << std::endl;
	});

	signal(20);
	signal(50);
	connection.Disconnect();
}

TESTCASE(ThreadPoolTest) {
	NahidaProject::ThreadPool::SafeQueue::ThreadPool pool{};

	constexpr float num1 = 12.25f;
	constexpr float num2 = 17.75f;
	constexpr int scalar = 2;
	auto foo = pool.Enqueue([num1] { 
		return num1 * static_cast<float>(scalar); 
	});

	auto bar = pool.Enqueue(Calculate, num2, scalar);

	const auto result_foo = foo.get();
	const auto result_bar = bar.get();

	JUDGE_EQUAL(result_foo, 24.5);
	JUDGE_EQUAL(result_bar, 35.5);
	JUDGE_EQUAL(result_foo + result_bar, 60);
}

int main() {
	NahidaProject::NahidaUnitTest::RunAllTests();
	return 0;
}